import asyncio


class BackgroundTaskMixin:
    def _set_default_attr(self, name, value):
        if not hasattr(self, name):
            setattr(self, name, value)

    def get_running_background_task(self, name):
        running_task = self.background_tasks.get(name)
        if running_task:
            if running_task.done() or running_task.cancelled():
                self.background_tasks.pop(name)
                running_task = None
        return running_task

    def start_background_task(self, name, *args, **kwargs):
        self._set_default_attr('background_tasks', {})
        running_task = self.get_running_background_task(name)
        if running_task:
            self.logger.debug('background task %s already running', name)
            return
        current_loop = asyncio.get_event_loop()
        task = current_loop.create_task(
            self._background_task_wrapper(name, *args, **kwargs))
        self.background_tasks[name] = task
        self.logger.debug('background task %s is created', name)

    async def _background_task_wrapper(self, name, *args, **kwargs):
        handler = getattr(self, f'background_{name}', None)
        if not handler:
            msg = f'background task {name} handler does not exist '
            self.logger.error(msg)
            raise ValueError(msg)
        try:
            await handler(*args, **kwargs)
        except Exception as exc:
            self.logger.exception('background task %s raise exception: %s',
                                  handler, exc)

    def stop_background_task(self, name):
        running_task = self.get_running_background_task(name)
        if running_task:
            self.background_tasks.pop(name)
            return running_task.cancel()

    def stop_background_tasks(self):
        task_names = list(self.background_tasks.keys())
        for name in task_names:
            self.stop_background_task(name)
